自定义指令:复制指令
概述
Vue 自定义指令(Custom Directives)是 Vue 中非常强大的功能,可以在模板中像写属性一样使用,实现诸如复制、防抖、节流、长按、水印等实用功能。本节介绍如何使用 import.meta.glob 自动注册指令,并以复制指令(v-copy)为例进行实现。
指令自动注册
目录结构
src/directives/
├── index.ts ← 入口文件:自动加载所有指令
└── modules/
├── copy.ts ← v-copy 复制指令
├── debounce.ts ← v-debounce 防抖指令
├── throttle.ts ← v-throttle 节流指令
├── longpress.ts ← v-longpress 长按指令
└── watermark.ts ← v-watermark 水印指令
text
入口文件:使用 glob 自动注册
// directives/index.ts
import type { App, Directive } from 'vue'
/**
* 使用 import.meta.glob 自动加载 modules 目录下的所有指令文件
* 文件名即为指令名:copy.ts → v-copy
*/
const modules = import.meta.glob<{ default: Directive }>('./modules/*.ts', {
eager: true
})
export function setupDirectives(app: App) {
Object.entries(modules).forEach(([path, module]) => {
// 从路径提取指令名:'./modules/copy.ts' → 'copy'
const name = path
.replace('./modules/', '')
.replace('.ts', '')
// 注册为全局指令:v-copy
app.directive(name, module.default)
})
}
typescript
在 main.ts 中注册
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import { setupDirectives } from './directives'
const app = createApp(App)
// 注册所有自定义指令
setupDirectives(app)
app.mount('#app')
typescript
复制指令实现
// directives/modules/copy.ts
import type { Directive, DirectiveBinding } from 'vue'
import { ElMessage } from 'element-plus'
/**
* v-copy 指令
* 用法:<el-button v-copy="copyText">复制</el-button>
* 点击按钮时将 copyText 的值复制到剪贴板
*/
export const copy: Directive = {
mounted(el: HTMLElement, binding: DirectiveBinding<string>) {
el.style.cursor = 'pointer'
el.addEventListener('click', async () => {
const text = binding.value
if (!text) {
ElMessage.warning('复制内容为空')
return
}
try {
await navigator.clipboard.writeText(text)
ElMessage.success('复制成功')
} catch {
// 降级方案:使用 execCommand
fallbackCopy(text)
}
})
},
updated(el: HTMLElement, binding: DirectiveBinding<string>) {
// 更新复制内容
el._copyText = binding.value
}
}
/**
* 降级复制方案(兼容旧浏览器)
*/
function fallbackCopy(text: string) {
const textarea = document.createElement('textarea')
textarea.value = text
textarea.style.position = 'fixed'
textarea.style.opacity = '0'
document.body.appendChild(textarea)
textarea.select()
try {
document.execCommand('copy')
ElMessage.success('复制成功')
} catch {
ElMessage.error('复制失败,请手动复制')
} finally {
document.body.removeChild(textarea)
}
}
export default copy
typescript
使用方式
<template>
<!-- 基础用法:点击复制文本 -->
<el-button v-copy="apiKey">复制 API Key</el-button>
<!-- 复制变量值 -->
<span v-copy="userEmail">{{ userEmail }}</span>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const apiKey = ref('sk-abc123def456')
const userEmail = ref('user@example.com')
</script>
vue
其他常见指令规划
| 指令 | 文件 | 功能 |
|---|---|---|
v-copy | copy.ts | 点击复制文本到剪贴板 |
v-debounce | debounce.ts | 防抖:延迟执行点击事件 |
v-throttle | throttle.ts | 节流:固定间隔执行事件 |
v-longpress | longpress.ts | 长按触发事件 |
v-watermark | watermark.ts | Canvas 水印效果 |
v-permission | hasPermission.ts | 按钮级权限控制 |
剪贴板 API 对比
| 方案 | 兼容性 | 异步 | 说明 |
|---|---|---|---|
navigator.clipboard.writeText() | 现代浏览器 | 是 | 推荐方案 |
document.execCommand('copy') | 所有浏览器 | 否 | 降级方案(已废弃) |
实践要点
- 使用
import.meta.glob自动加载modules/目录下的指令文件,新指令只需创建文件即可自动注册 - 文件名即为指令名:
copy.ts生成v-copy,debounce.ts生成v-debounce - 复制指令优先使用
navigator.clipboardAPI,降级使用execCommand - 在
main.ts中调用setupDirectives(app)全局注册所有指令 - 后续添加新指令只需在
modules/目录下新建.ts文件,无需修改入口文件
↑